const touchEnabled = !!('ontouchstart' in window)

declare type JoyStickOption = {
    maxRadius?: number;
    maxRadiusSquared?: number;
    rotationDamping?: number;
    moveDamping?: number;
    onMove?: (forward: number, turn: number) => void;
    origin?: {
        left: number;
        top: number;
    };
    offset?: {
        x: number;
        y: number;
    };
}

const option = {
    maxRadius: 0,
    maxRadiusSquared: 0,
    rotationDamping: 0,
    moveDamping: 0,
    origin: {
        left: 0,
        top: 0,
    },
    offset: {
        x: 0,
        y: 0,
    },
    onMove: (forward: number, turn: number) => {},

};

export class JoyStick {
    domElement: HTMLElement;
    onTouchMoved = (e: TouchEvent|MouseEvent) => {};
    onTouchEnded = (e: TouchEvent|MouseEvent) => {};


    constructor(optionsC: JoyStickOption) {
        this.domElement = this.createDom();
        option.maxRadius = optionsC.maxRadius || 40;
        option.maxRadiusSquared = option.maxRadius * option.maxRadius;
        if (optionsC.onMove) {
            option.onMove = optionsC.onMove;
        }
        option.origin = {
            left: this.domElement.offsetLeft,
            top: this.domElement.offsetTop
        }
        option.rotationDamping = optionsC.rotationDamping || 0.06;
        option.moveDamping = optionsC.moveDamping || 0.01;
        this.createEvent();
    }

    createEvent() {
        const joystick = this
        if(touchEnabled) {
            this.domElement.addEventListener('touchstart', (e: TouchEvent) => {
                e.preventDefault();
                joystick.tap(e);
                e.stopPropagation();
            })
        } else {
            this.domElement.addEventListener('mousedown', (e: MouseEvent) => {
                e.preventDefault();
                joystick.tap(e);
                e.stopPropagation();
            })
        }
    }

    getMousePosition(e: TouchEvent|MouseEvent) {
        let clientX:number = 0;
        let clientY:number = 0;
        if ("targetTouches" in e) {
            clientX = e.targetTouches[0].pageX;
            clientY = e.targetTouches[0].pageY;
        }
        if ("clientX" in e) {
            clientX =  e.clientX;
            clientY =  e.clientY;
        }
        return {
            x:clientX,
            y:clientY
        }
    }

    tap(e: TouchEvent|MouseEvent) {
        option.offset = this.getMousePosition(e);
        const joystick = this;
        this.onTouchMoved = (e: TouchEvent|MouseEvent) => {
            e.preventDefault();
            joystick.move(e);
        }
        this.onTouchEnded = (e: TouchEvent|MouseEvent) => {
            e.preventDefault();
            joystick.up(e);
        }
        if(touchEnabled) {
            document.addEventListener('touchmove', this.onTouchMoved);
            document.addEventListener('touchend', this.onTouchEnded);
        } else {
            document.addEventListener('mousemove', this.onTouchMoved);
            document.addEventListener('mouseup', this.onTouchEnded);
        }
    }

    move(e: TouchEvent|MouseEvent) {
        const mouse = this.getMousePosition(e)

        let left = mouse.x - option.offset!.x
        let top = mouse.y - option.offset!.y

        const sqMag = left * left + top * top

        if (sqMag > option.maxRadiusSquared){
            const magnitude = Math.sqrt(sqMag)
            left /= magnitude;
            top /= magnitude;
            left *= option.maxRadius!;
            top *= option.maxRadius!;
        }

        this.domElement.style.top = `${ top + this.domElement.clientHeight / 2 }px`;
        this.domElement.style.left = `${ left + this.domElement.clientWidth / 2 }px`;

        const forward = -(top - option.origin!.top + this.domElement.clientHeight / 2) / option.maxRadius;
        const turn = (left - option.origin!.left + this.domElement.clientWidth / 2) / option.maxRadius;

        if(option.onMove) {
            option.onMove(forward, turn)
        }

    }

    up(e: TouchEvent|MouseEvent) {
        if (touchEnabled){
            document.removeEventListener('touchmove', this.onTouchMoved)
            document.removeEventListener('touchend', this.onTouchEnded)
        }else{
            document.removeEventListener('mousemove', this.onTouchMoved)
            document.removeEventListener('mouseup', this.onTouchEnded)
        }
        this.domElement.style.top = `${option.origin!.top}px`
        this.domElement.style.left = `${option.origin!.left}px`
        if(option.onMove) {
            option.onMove(0, 0)
        }
    }

    createDom() {
        const circle = document.createElement('div' );
        circle.style.cssText = `position: absolute;bottom: 35px;width: 80px;height: 80px;background: rgba(126, 126, 126, 0.2);border: #444 solid medium;border-radius: 50%;left: 50%;transform: translateX(-50%);`;
        const thumb = document.createElement('div');
        thumb.style.cssText = `position: absolute;left: 20px;top: 20px;width: 40px;height: 40px;border-radius: 50%;background: #fff;`;
        circle.appendChild(thumb);
        document.body.appendChild(circle);
        return thumb;
    }
}